﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;
using System.Collections;

namespace PO_2011.Filter
{
    class Filter
    {
        private static int[,] maskx;
        private static int[,] masky;

        private static String mask_type;

        //ile jest okolicznych piksel, np. maska size 3, ma z kazdej strony 1 piksel, maska 9, ma 4 z kazdej strony
        private static int minus;

        private static Bitmap copy_image;

        public static Bitmap create(Bitmap image, String maskType, int mask_size)
        {
            copy_image = new Bitmap(image);
            mask_type = maskType;

            createMask(ref mask_size);

            minus = (int) (mask_size / 2);

            if (mask_type == "odszumianie_mediana")
            {
                removeNoise(ref image, ref mask_size);
            }
            else if (mask_type == "sobel" | mask_type == "previtt" | mask_type == "roberts" | mask_type == "LoG")
            {
                makeEdges(ref image, ref mask_size);
            }
            else
            {
                makeFilter(ref image, ref mask_size);
            }

            return copy_image;
        }


        private static void createMask(ref int mask_size)
        {
            maskx = new int[mask_size, mask_size];

            switch (mask_type)
            {
                case "jednolite":
                    for (int i = 0; i < mask_size; i++)
                    {
                        for (int j = 0; j < mask_size; j++)
                        {
                            maskx[i, j] = 1;
                        }
                    }
                    break;
                case "gauss":
                    if (mask_size == 3)
                    {
                        maskx = new int[3, 3] { { 1, 2, 1 }, { 2, 4, 2 }, { 1, 2, 1 } };
                    }
                    else if (mask_size == 5)
                    {
                        maskx = new int[5, 5] { { 1, 2, 4, 2, 1 }, { 2, 4, 8, 4, 2 }, { 4, 8, 16, 8, 4 }, { 2, 4, 8, 4, 2 }, { 1, 2, 4, 2, 1 } };
                    }
                    break;
                case "sobel":
                    maskx = new int[3, 3] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };
                    masky = new int[3, 3] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } };
                    break;
                case "previtt":
                    maskx = new int[3, 3] { { -1, 0, 1 }, { -1, 0, 1 }, { -1, 0, 1 } };
                    masky = new int[3, 3] { { 1, 1, 1 }, { 0, 0, 0 }, { -1, -1, -1 } };
                    break;
                case "roberts":
                    maskx = new int[2, 2] { { 1, 0 }, { 0, -1 }};
                    masky = new int[2, 2] { { 0, 1}, { -1, 0}};
                    break;
                case "odszumianie_mediana":
                    maskx = new int[3, 3] { { 11, 21, 78}, { 21, 256, 19 }, {44, 33, 76} };
                    break;
                case "LoG":
                    /*
                    maskx = new int[3, 3] { { 0, 1, 0 }, { 1, -4, 1 }, { 0, 1, 0 } };
                    masky = new int[3, 3] { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
                    */
                    maskx = new int[9, 9] { { 0, 1, 1, 2, 2, 2, 1, 1, 0 }, { 1, 2, 4, 5, 5, 5, 4, 2, 1 }, { 1, 4, 5, 3, 0, 3, 5, 4, 1 }, { 2, 5, 3, -12, -24, -12, 3, 5, 2 }, { 2, 5, 0, -24, -40, -24, 0, 5, 2 }, { 2, 5, 3, -12, -24, -12, 3, 5, 2 }, { 1, 4, 5, 3, 0, 3, 5, 4, 1 }, { 1, 2, 4, 5, 5, 5, 4, 2, 1 }, { 0, 1, 1, 2, 2, 2, 1, 1, 0 } };
                    masky = new int[9, 9] { { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
                    
                    break;
                default: // general
                    String[] mask_arr = mask_type.Split(';');
                    String[] mask_arr2;

                    mask_size = mask_arr.Length;

                    maskx = new int[mask_size, mask_size];

                    for (int i = 0; i < mask_size; i++)
                    {
                        mask_arr2 = mask_arr[i].Split(',');

                        for (int j = 0; j < mask_size; j++)
                        {
                            maskx[i, j] = Convert.ToInt32(mask_arr2[j]);
                        }
                    }
                    break;
            }
        }
        private static void removeNoise(ref Bitmap image, ref  int mask_size)
        {
            int xx = 0, yy = 0, sR, sG, sB, noise = 100;
            int[] median = new int[3] { 0, 0, 0 };

            Color color;

            for (int x = 0; x < image.Width; x++)
            {
                for (int y = 0; y < image.Height; y++)
                {
                    // index idzie pionowo po zdjeciu, np. 1, 101, 201, 2, 102, 202, ...
                    sR = sG = sB = 0;

                    for (int i = 0; i < mask_size; i++)
                    {
                        for (int j = 0; j < mask_size; j++)
                        {
                            if (i == 1 && j == 1) continue;

                            if (x - i + 1 < 0)
                            {
                                xx = 0;
                            }
                            else if (x - i + 1 >= image.Width)
                            {
                                xx = image.Width - 1;
                            }
                            else
                            {
                                xx = x - i + 1;
                            }
                            if (y - j + 1 < 0)
                            {
                                yy = 0;
                            }
                            else if (y - j + 1 >= image.Height)
                            {
                                yy = image.Height - 1;
                            }
                            else
                            {
                                yy = y - j + 1;
                            }

                            color = image.GetPixel(xx, yy);
                            sR += (int)color.R;
                            sG += (int)color.G;
                            sB += (int)color.B;
                        }
                    }

                    color = image.GetPixel(x, y);
                    median[0] = (int)(sR / (mask_size * mask_size - 1));
                    median[1] = (int)(sG / (mask_size * mask_size - 1));
                    median[2] = (int)(sB / (mask_size * mask_size - 1));

                    if ((median[0] + noise < color.R) || (median[0] > color.R + noise) || (median[1] + noise < color.G) || (median[1] > color.G + noise) || (median[2] + noise < color.B) || (median[2] > color.B + noise))
                    {
                        copy_image.SetPixel(x, y, Color.FromArgb(median[0], median[1], median[2]));
                    }
                    else
                    {
                        copy_image.SetPixel(x, y, Color.FromArgb(color.R, color.G, color.B));
                    }
                }
            }
        }

        private static void makeFilter(ref Bitmap image, ref  int mask_size)
        {
            int xx = 0, yy = 0, sR, sG, sB;
            int sum = 0;

            Color color;

            for (int i = 0; i < mask_size; i++)
            {
                for (int j = 0; j < mask_size; j++)
                    sum += maskx[i, j];
            }

            if (sum == 0) sum = 1;


            for (int x = 0; x < image.Width; x++)
            {
                for (int y = 0; y < image.Height; y++)
                {
                    // index idzie pionowo po zdjeciu, np. 1, 101, 201, 2, 102, 202, ...
                    sR = sG = sB = 0;

                    for (int i = 0; i < mask_size; i++)
                    {
                        for (int j = 0; j < mask_size; j++)
                        {
                            if(x - i + 1 < 0)
                            {
                                xx = 0;
                            }
                            else if (x - i + 1 >= image.Width)
                            {
                                xx = image.Width - 1;
                            }
                            else
                            {
                                xx = x - i + 1;
                            }
                            if (y - j + 1 < 0)
                            {
                                yy = 0;
                            }
                            else if (y - j + 1 >= image.Height)
                            {
                                yy = image.Height - 1;
                            }
                            else
                            {
                                yy = y - j + 1;
                            }

                            color = image.GetPixel(xx, yy);
                            sR += maskx[i, j] * (int)color.R;
                            sG += maskx[i, j] * (int)color.G;
                            sB += maskx[i, j] * (int)color.B;
                        }
                    }

                    copy_image.SetPixel(x, y, Color.FromArgb(sR / sum, sG / sum, sB / sum));

                }
            }
        }

        private static void makeEdges(ref Bitmap image, ref  int mask_size)
        {
            int[] s; 
            int grey;

            for (int x = 1; x < image.Width - 1; x++)
            {
                for (int y = 1; y < image.Height - 1; y++)
                {
                    // index idzie pionowo po zdjeciu, np. 1, 101, 201, 2, 102, 202, ...
                    s = createColorForEdges(image, x, y, mask_size);

                    grey = Math.Abs(s[0]) + Math.Abs(s[1]);

                    if (grey > 255) grey = 255;
                    else if (grey < 0) grey = 0;

                    copy_image.SetPixel(x, y, Color.FromArgb(grey, grey, grey));
                }
            }
        }

        private static int [] createColorForEdges(Bitmap image, int x, int y, int mask_size)
        {
            int xx = 0, yy = 0;
            int [] grey = new int [2] {0, 0};

            for (int i = 0; i < mask_size; i++)
            {
                for (int j = 0; j < mask_size; j++)
                {
                    xx = x + i - minus;
                    yy = y + j - minus;

                    if (xx >= image.Width | xx < 0 | yy >= image.Height | yy < 0) continue;

                    grey[0] += maskx[j, i] * (int)image.GetPixel(xx, yy).R;
                    grey[1] += masky[j, i] * (int)image.GetPixel(xx, yy).R;
                }
            }

            return grey;
        }

    }
}
